/**
 * This file is part of JNotifyOSD.
 *
 * JNotifyOSD is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JNotifyOSD is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with JNotifyOSD. If not, see <http://www.gnu.org/licenses/>.
 */

#include <jni.h>
#include <libnotify/notify.h>

void Java_notifyosd_NativeNotificationService_nativeInit(
		JNIEnv* env, jobject obj, jstring app_name)
{
	const char* c_app_name = (*env)->GetStringUTFChars(env, app_name, NULL);
	if (notify_init(c_app_name) == FALSE)
	{
		(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeException"), "Initialization failed");
		return;
	}
	
	(*env)->ReleaseStringUTFChars(env, app_name, c_app_name);
}

void Java_notifyosd_NativeNotificationService_nativeUninit(JNIEnv* env, jobject obj)
{
	notify_uninit();
}

jboolean Java_notifyosd_NativeNotificationService_nativeIsInitted(JNIEnv* env, jobject obj)
{
	return (notify_is_initted() == TRUE) ? JNI_TRUE : JNI_FALSE;
}

struct
server_caps_data
{
	JNIEnv* env;
	guint i;
	jobjectArray j_caps;
};

static
void add_capty_to_jarray(gchar* capacity, struct server_caps_data* data)
{
	JNIEnv* env = data->env;
	jstring s = (*env)->NewStringUTF(env, capacity);
	(*env)->SetObjectArrayElement(env, data->j_caps, (data->i)++, s);
	g_free(capacity);
}

jobjectArray Java_notifyosd_NativeNotificationService_nativeGetServerCaps(JNIEnv* env, jobject obj)
{
	GList* g_caps = notify_get_server_caps();
	guint caps_len = g_list_length(g_caps);
	struct server_caps_data data;
	data.env = env;
	data.j_caps = (*env)->NewObjectArray(env,
			(jint)caps_len,
			(*env)->FindClass(env, "java/lang/String"),
			(*env)->NewStringUTF(env, ""));
	data.i = 0;
	g_list_foreach(g_caps, (GFunc)add_capty_to_jarray, &data);
	g_list_free(g_caps);
	return data.j_caps;
}
                                        
jobjectArray Java_notifyosd_NativeNotificationService_nativeGetServerInfo(JNIEnv* env, jobject obj)
{
	char* data[4];
	guint i;
	
	gboolean success = notify_get_server_info(&data[0], &data[1], &data[2], &data[3]);
	
	if (success == FALSE)
	{
		(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeException"), "Information retrieval failed");
		return NULL;
	}
	
	jobjectArray res = (*env)->NewObjectArray(env, 4, (*env)->FindClass(env, "java/lang/String"), (*env)->NewStringUTF(env, ""));
	for (i = 0; i < 4; ++i)
	{
		(*env)->SetObjectArrayElement(env, res, i, (*env)->NewStringUTF(env, data[i]));
		g_free(data[i]);
	}
	
	return res;
}

jstring Java_notifyosd_NativeNotificationService_nativeGetAppName(JNIEnv* env, jobject obj)
{
	const gchar* appName = notify_get_app_name();
	jstring res = (*env)->NewStringUTF(env, appName);
	return res;
}

////////////////////////////////////////////////////////////////////////

static
jfieldID nativeNotificationID(JNIEnv* env)
{
	static jfieldID fId = NULL;
	
	if (fId == NULL)
	{
		fId = (*env)->GetFieldID(env, (*env)->FindClass(env, "notifyosd/NativeNotification"), "nativeNotification", "J");
		if (fId == NULL)
		{
			jthrowable t = (*env)->ExceptionOccurred(env);
			if (t)
				(*env)->Throw(env, t);
			else
				(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeError"), "Field nativeNotification not found");
			return NULL;
		}		
	}
	
	return fId;
}

void Java_notifyosd_NativeNotification_nativeInit(
		JNIEnv* env,
		jobject obj,
		jstring summary,
		jstring body,
		jstring icon) {
	
	const char* c_summary = (*env)->GetStringUTFChars(env, summary, NULL);
	const char* c_body = (body) ? (*env)->GetStringUTFChars(env, body, NULL) : NULL;
	const char* c_icon = (icon) ? (*env)->GetStringUTFChars(env, icon, NULL) : NULL;
	
	NotifyNotification* n = notify_notification_new(c_summary, c_body, c_icon);
	
	(*env)->ReleaseStringUTFChars(env, summary, c_summary);
	if (c_body)
		(*env)->ReleaseStringUTFChars(env, body, c_body);
	if (c_icon)
		(*env)->ReleaseStringUTFChars(env, icon, c_icon);
		
	if (n == NULL)
	{
		(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeError"), "Native notification creation failed");
	}
		
	jfieldID fId = nativeNotificationID(env);
	if (fId == NULL)
	{
		g_object_unref(n);
		return;
	}
	
	(*env)->SetLongField(env, obj, fId, (jlong)n);
}

void Java_notifyosd_NativeNotification_nativeDestroy(JNIEnv* env, jobject obj)
{
	jfieldID fId = nativeNotificationID(env);
	if (fId == NULL) return;
	
	NotifyNotification* n = (NotifyNotification*)(*env)->GetLongField(env, obj, fId);
	g_object_unref(n);
}


void Java_notifyosd_NativeNotification_nativeUpdate(
		JNIEnv* env,
		jobject obj,
		jstring summary,
		jstring body,
		jstring icon)
{
	const char* c_summary = (*env)->GetStringUTFChars(env, summary, NULL);
	const char* c_body = (body) ? (*env)->GetStringUTFChars(env, body, NULL) : NULL;
	const char* c_icon = (icon) ? (*env)->GetStringUTFChars(env, icon, NULL) : NULL;
	
	jfieldID fId = nativeNotificationID(env);
	
	NotifyNotification* n = (NotifyNotification*)(*env)->GetLongField(env, obj, fId);
	
	if (notify_notification_update(n, c_summary, c_body, c_icon) == FALSE)
	{
		(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeException"), "Native notification update failed");
	}
	
	(*env)->ReleaseStringUTFChars(env, summary, c_summary);
	if (c_body)
		(*env)->ReleaseStringUTFChars(env, body, c_body);
	if (c_icon)
		(*env)->ReleaseStringUTFChars(env, icon, c_icon);
}

void Java_notifyosd_NativeNotification_nativeShow(JNIEnv* env, jobject obj)
{
	jfieldID fId = nativeNotificationID(env);
	if (fId == NULL) return;
	
	NotifyNotification* n = (NotifyNotification*)(*env)->GetLongField(env, obj, fId);
	GError* err = NULL;
	
	if (notify_notification_show(n, &err) == FALSE)
	{
		(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeException"), err->message);
		g_error_free(err);
	}
}

void Java_notifyosd_NativeNotification_nativeSetTimeout(
		JNIEnv* env, jobject obj, jint timeout)
{
	jfieldID fId = nativeNotificationID(env);
	if (fId == NULL) return;
	
	NotifyNotification* n = (NotifyNotification*)(*env)->GetLongField(env, obj, fId);
	notify_notification_set_timeout(n, (gint)timeout);
}

void Java_notifyosd_NativeNotification_nativeSetCategory(
		JNIEnv* env, jobject obj, jstring category)
{
	jfieldID fId = nativeNotificationID(env);
	if (fId == NULL) return;
	
	NotifyNotification* n = (NotifyNotification*)(*env)->GetLongField(env, obj, fId);
	const char* c_cat = (*env)->GetStringUTFChars(env, category, NULL);
	notify_notification_set_category(n, c_cat);
	(*env)->ReleaseStringUTFChars(env, category, c_cat);
}

jint Java_notifyosd_NativeNotification_getLowPrio(JNIEnv* env, jobject obj)
{
	return NOTIFY_URGENCY_LOW;
}

jint Java_notifyosd_NativeNotification_getNorPrio(JNIEnv* env, jobject obj)
{
	return NOTIFY_URGENCY_NORMAL;
}

jint Java_notifyosd_NativeNotification_getCriPrio(JNIEnv* env, jobject obj)
{
	return NOTIFY_URGENCY_CRITICAL;
}

void Java_notifyosd_NativeNotification_nativeSetUrgency(
		JNIEnv* env, jobject obj, jint urgency)
{
	jfieldID fId = nativeNotificationID(env);
	if (fId == NULL) return;
	
	NotifyNotification* n = (NotifyNotification*)(*env)->GetLongField(env, obj, fId);
	notify_notification_set_urgency(n, (NotifyUrgency)urgency);
}

void Java_notifyosd_NativeNotification_nativeHide(
		JNIEnv* env, jobject obj)
{
	jfieldID fId = nativeNotificationID(env);
	if (fId == NULL) return;
	
	NotifyNotification* n = (NotifyNotification*)(*env)->GetLongField(env, obj, fId);
	GError* err = NULL;
	
	if (notify_notification_close(n, &err) == FALSE)
	{
		(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeException"), err->message);
		g_error_free(err);
	}
}

/* YET UNIMPLEMENTED METHODS
gint                notify_notification_get_closed_reason     (const NotifyNotification *notification);

void                notify_notification_set_geometry_hints    (NotifyNotification *notification,
                                                               GdkScreen          *screen,
                                                               gint                x,
                                                               gint                y);

void                notify_notification_set_icon_from_pixbuf  (NotifyNotification *notification,
                                                               GdkPixbuf          *icon);
void                notify_notification_set_image_from_pixbuf (NotifyNotification *notification,
                                                               GdkPixbuf          *image);

void                notify_notification_set_hint_int32        (NotifyNotification *notification,
                                                               const char         *key,
                                                               gint                value);
void                notify_notification_set_hint_uint32       (NotifyNotification *notification,
                                                               const char         *key,
                                                               guint               value);

void                notify_notification_set_hint_double       (NotifyNotification *notification,
                                                               const char         *key,
                                                               gdouble             value);

void                notify_notification_set_hint_string       (NotifyNotification *notification,
                                                               const char         *key,
                                                               const char         *value);

void                notify_notification_set_hint_byte         (NotifyNotification *notification,
                                                               const char         *key,
                                                               guchar              value);

void                notify_notification_set_hint_byte_array   (NotifyNotification *notification,
                                                               const char         *key,
                                                               const guchar       *value,
                                                               gsize               len);

void                notify_notification_clear_hints           (NotifyNotification *notification);

void                notify_notification_add_action            (NotifyNotification *notification,
                                                               const char         *action,
                                                               const char         *label,
                                                               NotifyActionCallback callback,
                                                               gpointer            user_data,
                                                               GFreeFunc           free_func);

void                notify_notification_clear_actions         (NotifyNotification *notification);
*/

/*
int main()
{
	printf("%d\n", notify_init("prova"));
	
	GList* g_caps = notify_get_server_caps();
	printf("%p\n", g_caps);
	
	gpointer data = g_list_nth_data(g_caps, 0);
	printf("%p\n", data);
	printf("%s\n", (gchar*)data);
	
	notify_uninit();
	return 0;
}
*/
